#ifndef __GAME_H
#define __GAME_H

#include "timesystem.h"
/*
#include "rendermanager.h"
#include "textureloader.h"
#include "audioloader.h"
#include "audioplayer.h"
*/
#include "inputdevices.h"
#include "config.h"

#include "scene.h"

#include <list>

/**
 * This is the main class in the framework.
 * It's a singleton class that has the principal components:
 * the render, the audio system, the input system, the settings and a scene
 * FIFO. Each scene represent a part of the game, like the main menu, a story
 * board or a stage. There is a actual scene which is the only one updated.
 * The function load initialize all the components, later you can modify
 * and add everything you want like the main scene. When you call start
 * the main loop beings. In the main loop all the system are updated and the
 * actual scene. The main loop ends when there isn't any scene or setEnd()
 * is called.
 */
class Game
{
  public:
    /**
      * Singleton Class
      *
      * @returns the only one instance of the class
      */
    static Game& self();

    /**
      * Free all the allocated memory and unload all the system like
      * SDL or OpenGL.
      */
    ~Game();

    ////////////////////////////
    // MAIN COMPONENTES
    ////////////////////////////
    /**
      * @returns The render
    RenderManager& getRenderManager()const;
      */

    /**
      * @returns The texture manager
    TextureLoader& getTextureLoader()const;
      */

    /**
      * @returns The audio player
    AudioPlayer& getAudioPlayer()const;
      */

    /**
      * @returns The sound manager
    AudioLoader& getAudioLoader()const;
      */

    /**
      * @returns The input system
      */
    InputDevices& getInputDevices()const;

    /**
      * @returns The general settings
      */
    Config& getSetting()const;

    /**
      * Load the all the system like SDL and OpenGL. Uses settings to
      * configure them. In the case there isn't a config file it filled
      * with the default settings.
      *
      * @returns True if everything is OK or false if something couldn't
      * be loaded and the program can't go on.
      */
    bool load();

    /**
      * If the setting has been modified and you want to reload the
      * new setting this funtion must be called
      *
      * @returns True if everything is OK or false if something couldn't
      * be loaded and the program can't go on.
      */
    bool reload();

    /**
      * With this function the game will go in the main loop, so when
      * it would be called it never returns until the game ends.
      */
    void start();

    /**
      * @returns The actual scene. If there isn't any scene a new one
      * will be created and returned.
      */
    Scene& getActualScene()const;

    /**
      * Add a new scene to the FIFO. This new scene will be the actual
      * scene, so the previous scene won't be updated again until this
      * scene have been poped.
      * 
      * @param sc is the scene to add
      */
    void pushScene(Scene* sc);

    /**
      * Remove the actual scene in the next loop. All the memory allocated
      * by this scene will be freed and couldn't be recovery. The actual scene will be
      * now the previous scene. If there wouldn't be the game will finish.
      */
    void popScene();

    /**
      * @returns How many scene there are actually in the FIFO.
      */
    unsigned getSceneNumber()const;

    /**
      * The main loop will be finished and all the memory will be freed
      */
    bool setEnd();

  protected:
    /**
      * Constructor. Create only the setting.
      */
    Game();

    /**
      * Unload all the systems and allocated memory.
      */
    void unload();

    /**
      * Set window's name and icon
      */
    void setWindowNameIcon();

    static Game* m_self;

    /*
    RenderManager* m_renderManager;
    TextureLoader* m_textureLoader;
    AudioPlayer* m_audioPlayer;
    AudioLoader* m_audioLoader;
    */
    InputDevices* m_inputDevices;
    TimeSystem* m_timeSystem;
    Config* m_setting; 

    typedef std::list<Scene*> sceneList;
    typedef sceneList::iterator sceneIt;
    sceneList m_scene;
    sceneIt m_actualScene;

    bool m_end;
};

#endif
